home *** CD-ROM | disk | FTP | other *** search
/ MacHack 1999 / MacHack 1999.toast / The Hacks / BlueBox Spy / Blue Box Daemon / source / CTerminalPane.cp < prev    next >
Encoding:
Text File  |  1998-08-10  |  16.0 KB  |  630 lines  |  [TEXT/CWIE]

  1. // ===========================================================================
  2. //    CTerminalPane.cp           ©1996-1998 Metrowerks Inc. All rights reserved.
  3. // ===========================================================================
  4.  
  5. #include "CTerminalPane.h"
  6.  
  7. #include <Sound.h>
  8. #include <LowMem.h>
  9.  
  10. #define resetInvalsChar 3
  11.  
  12.  
  13. // ===========================================================================
  14. //        • Display parameters
  15. // ===========================================================================
  16. //    Most of the appearance of this pane is hard-wired. It works for the
  17. //    simple demonstration we're doing here, but isn't intended for industrial-
  18. //    strength work. :-)
  19.  
  20. enum {
  21.     pixelsX = 6,                    // default font char width (Monaco 9)
  22.     pixelsY = 11,                    // default font char height
  23.     
  24.     sizeX = 488,                    // 80 columns * 6 pixels + 8 margin
  25.     sizeY = 272                        // 24 rows * 11 pixels + 8 margin
  26. };
  27.  
  28. // ===========================================================================
  29. //        • Standard ASCII keycodes
  30. // ===========================================================================
  31.  
  32. enum {
  33.     charNUL = 0,
  34.     charBEL = 7,
  35.     charBS,
  36.     charHT,
  37.     charLF,
  38.     charVT,
  39.     charFF,
  40.     charCR,
  41.     charDEL = 127
  42. };
  43.  
  44.  
  45. // ===========================================================================
  46.  
  47. #pragma mark ••• CTerminalPane •••
  48.  
  49.  
  50. // ---------------------------------------------------------------------------
  51. //        • CTerminalPane
  52. // ---------------------------------------------------------------------------
  53. //    Stream constructor
  54.  
  55. CTerminalPane::CTerminalPane(
  56.     PP_PowerPlant::LStream* inStream)
  57.         : LView(inStream)
  58. {
  59.     InitTerm();
  60. }
  61.  
  62.  
  63. // ---------------------------------------------------------------------------
  64. //        • CTerminalPane
  65. // ---------------------------------------------------------------------------
  66. //    Destructor
  67.  
  68. CTerminalPane::~CTerminalPane()
  69. {
  70. }
  71.  
  72.  
  73. // ===========================================================================
  74.  
  75. #pragma mark -
  76. #pragma mark •• terminal primitives
  77.  
  78. // ---------------------------------------------------------------------------
  79. //        • DoWriteChar
  80. // ---------------------------------------------------------------------------
  81. //    Write a single character to the terminal.
  82.  
  83. void
  84. CTerminalPane::DoWriteChar(
  85.     char inChar)
  86. {
  87.  
  88.     // Parse a few control characters.
  89.  
  90.     switch (inChar) {
  91.  
  92.         case charNUL:
  93.             break;
  94.  
  95.         case charBEL:
  96.             ::SysBeep(0);
  97.             break;
  98.  
  99.         case charBS:
  100.             if (mCurrentCursor.col > 0)
  101.                 mCurrentCursor.col--;
  102.             CursorMoved();
  103.             break;
  104.  
  105.         case charHT:
  106.             mCurrentCursor.col = (static_cast<SInt16>(((mCurrentCursor.col + 7) / 8))) * 8;
  107.             if (mCurrentCursor.col >= maxX)
  108.                 mCurrentCursor.col = maxX-1;
  109.             CursorMoved();
  110.             break;
  111.             
  112.         case charLF:
  113.             if (mCurrentCursor.row < maxY-1)
  114.                 mCurrentCursor.row++;
  115.             else
  116.                 ScrollTerm();
  117.             CursorMoved();
  118.             break;
  119.             
  120.         case charFF:
  121.             DoClearScreen();
  122.             break;
  123.             
  124.         case charCR:
  125.             mCurrentCursor.col = 0;
  126.             CursorMoved();
  127.             break;
  128.         
  129.         default:
  130.             PutCharAt(mCurrentCursor, inChar);
  131.             InvalChar(mCurrentCursor);
  132.             if (mCurrentCursor.col < maxX-1)
  133.                 mCurrentCursor.col++;
  134.             CursorMoved();
  135.     }
  136. }
  137.  
  138.  
  139. // ---------------------------------------------------------------------------
  140. //        • DoWriteStr
  141. // ---------------------------------------------------------------------------
  142. //    Print a C string to the terminal.
  143.  
  144. void
  145. CTerminalPane::DoWriteStr(
  146.     const char* inString)                // the string to be printed
  147. {
  148.  
  149.     // Optimize for text characters.
  150.     
  151.     while (*inString) {
  152.         if (*inString >= ' ') {
  153.             TermCharT originalChar = mCurrentCursor;
  154.             while (*inString >= ' ') {
  155.                 PutCharAt(mCurrentCursor, *(inString++));
  156.                 if (mCurrentCursor.col < maxX-1)
  157.                     mCurrentCursor.col++;
  158.             }
  159.             InvalChar(originalChar, mCurrentCursor.col - originalChar.col);
  160.             CursorMoved();
  161.         }
  162.         if (*inString)
  163.             DoWriteChar(*(inString++));
  164.     }
  165. }
  166.  
  167.  
  168. // ---------------------------------------------------------------------------
  169. //        • DoWriteBfr
  170. // ---------------------------------------------------------------------------
  171. //    Write a string of arbitrary length to the buffer.
  172.  
  173. void
  174. CTerminalPane::DoWriteBfr(
  175.     const char*    inBuffer,
  176.     SInt32        inByteCount)
  177. {
  178.     while (inByteCount--)
  179.         DoWriteChar(*(inBuffer++));
  180. }
  181.  
  182.  
  183. // ---------------------------------------------------------------------------
  184. //        • DoWriteCharNum
  185. // ---------------------------------------------------------------------------
  186. //    A debugging routine. Writes a character number in decimal with
  187. //    optional brackets (i.e. [240]).
  188.  
  189. void
  190. CTerminalPane::DoWriteCharNum(
  191.     char    inChar,
  192.     char    inLeftBracket,
  193.     char    inRightBracket)
  194. {
  195.     Str255 cNumber;
  196.  
  197.     if (inLeftBracket)
  198.         DoWriteChar(inLeftBracket);
  199.  
  200.     ::NumToString(static_cast<SInt32>(inChar), cNumber);
  201.     cNumber[cNumber[0]+1] = '\0';
  202.     DoWriteStr((char*) (&cNumber)+1);
  203.  
  204.     if (inRightBracket)    
  205.         DoWriteChar(inRightBracket);
  206. }
  207.  
  208.  
  209. // ---------------------------------------------------------------------------
  210. //        • DoClearScreen
  211. // ---------------------------------------------------------------------------
  212. //    Clear the entire screen display and reset the cursor to the upper left corner
  213.  
  214. void
  215. CTerminalPane::DoClearScreen()
  216. {
  217.     mCurrentCursor.row = mCurrentCursor.col = 0;
  218.     ClearToEOS(mCurrentCursor);
  219.     CursorMoved();
  220. }
  221.  
  222.  
  223. // ===========================================================================
  224.  
  225. #pragma mark -
  226. #pragma mark •• configuration
  227.  
  228. // ---------------------------------------------------------------------------
  229. //        • SetBlinking
  230. // ---------------------------------------------------------------------------
  231.  
  232. void
  233. CTerminalPane::SetBlinking(
  234.     bool inBlinkMode)
  235. {
  236.     mBlinkCursor = inBlinkMode;
  237.     if ((inBlinkMode) && (IsActive())) {
  238.         mPreviousCursor = mCurrentCursor;
  239.         CursorMoved();
  240.     }
  241.     else {
  242.         mCursorBlinkVisible = true;
  243.         InvalChar(mCurrentCursor);
  244.     }
  245. }
  246.  
  247.  
  248. // ===========================================================================
  249.  
  250. #pragma mark -
  251. #pragma mark •• drawing
  252.  
  253. // ---------------------------------------------------------------------------
  254. //        • DrawSelf
  255. // ---------------------------------------------------------------------------
  256. //    Drawing code adapted from LTable::DrawSelf.
  257.  
  258. void
  259. CTerminalPane::DrawSelf()
  260. {
  261.  
  262.     // Reset the invalidate optimization.
  263.  
  264.     mCharsToInvalLine = resetInvalsChar;
  265.  
  266.     // Determine cells that need updating. Rather than checking
  267.     // on a character by character basis, we just see which cells intersect
  268.     // the bounding box of the update region. This is relatively
  269.     // fast, but may result in unnecessary cell updates for
  270.     // non-rectangular update regions.
  271.                                 
  272.     RgnHandle localUpdateRgnH = GetLocalUpdateRgn();
  273.     Rect updateRect = (**localUpdateRgnH).rgnBBox;
  274.     ::DisposeRgn(localUpdateRgnH);
  275.     
  276.     // Find character at top left of update rect.
  277.  
  278.     PP_PowerPlant::SPoint32 topLeftUpdate;
  279.     TermCharT topLeftChar;
  280.     LocalToImagePoint(topLeft(updateRect), topLeftUpdate);
  281.     FetchCharHitBy(topLeftUpdate, topLeftChar);
  282.  
  283.     if (topLeftChar.row < 0)
  284.         topLeftChar.row = 0;
  285.     if (topLeftChar.col < 0)
  286.         topLeftChar.col = 0;
  287.  
  288.     // Find cell at bottom right of update rect.
  289.  
  290.     PP_PowerPlant::SPoint32 botRightUpdate;
  291.     TermCharT botRightChar;
  292.     LocalToImagePoint(botRight(updateRect), botRightUpdate);
  293.     FetchCharHitBy(botRightUpdate, botRightChar);
  294.  
  295.     if (botRightChar.row >= mRows)
  296.         botRightChar.row = mRows - 1;
  297.     if (botRightChar.col >= mCols)
  298.         botRightChar.col = mCols - 1;
  299.     
  300.     // Hard-wire for Monaco 9 (at least for now...).
  301.  
  302.     ::TextFont(4);
  303.     ::TextFace(0);
  304.     ::TextSize(9);
  305.  
  306.     // Draw each line within the update rect.
  307.  
  308.     TermCharT theChar;
  309.     theChar.col = topLeftChar.col;
  310.     
  311.     for (theChar.row = topLeftChar.row; theChar.row <= botRightChar.row; theChar.row++) {
  312.         Rect charFrame;
  313.         if (FetchLocalCharFrame(theChar, charFrame)) {
  314.             ::MoveTo(charFrame.left, charFrame.bottom - 2);        //! TEMPORARY: hard-code the baseline
  315.             ::DrawText(&mScreenBfr[theChar.row][theChar.col], 0, botRightChar.col - topLeftChar.col + 1);
  316.         }
  317.     }
  318.     
  319.     // If we overwrote the cursor, re-highlight it.
  320.  
  321.     if ((topLeftChar.col <= mCurrentCursor.col)
  322.                 && (botRightChar.col >= mCurrentCursor.col)
  323.                 && (topLeftChar.row <= mCurrentCursor.row)
  324.                 && (botRightChar.row >= mCurrentCursor.row)
  325.                 && mCursorBlinkVisible)
  326.         InvertChar(mCurrentCursor);
  327.     
  328. }
  329.  
  330.  
  331. // ===========================================================================
  332.  
  333. #pragma mark -
  334. #pragma mark •• blinking cursor support
  335.  
  336. // ---------------------------------------------------------------------------
  337. //        • ActivateSelf
  338. // ---------------------------------------------------------------------------
  339. //    Start the cursor blinking.
  340.  
  341. void
  342. CTerminalPane::ActivateSelf()
  343. {
  344.     mCursorBlinkVisible = true;
  345.     InvalChar(mCurrentCursor);
  346.     StartIdling();
  347. }
  348.  
  349.  
  350. // ---------------------------------------------------------------------------
  351. //        • DeactivateSelf
  352. // ---------------------------------------------------------------------------
  353. //    Stop cursor blinking.
  354.  
  355. void
  356. CTerminalPane::DeactivateSelf()
  357. {
  358.     StopIdling();
  359.     mCursorBlinkVisible = true;
  360.     InvalChar(mCurrentCursor);
  361. }
  362.  
  363.  
  364. // ---------------------------------------------------------------------------
  365. //        • SpendTime
  366. // ---------------------------------------------------------------------------
  367. //    This is where we cause the cursor to blink.
  368.  
  369. void
  370. CTerminalPane::SpendTime(
  371.     const EventRecord& /* inMacEvent */)
  372. {
  373.     if (!IsActive())
  374.         return;
  375.     if (mBlinkCursor) {
  376.         if (LMGetTicks() >= mCursorBlinkTick + GetCaretTime()) {
  377.             mCursorBlinkVisible = !mCursorBlinkVisible;
  378.             mCursorBlinkTick = LMGetTicks();
  379.             InvalChar(mCurrentCursor);
  380.         }
  381.     }
  382. }
  383.  
  384.  
  385. // ===========================================================================
  386.  
  387. #pragma mark -
  388. #pragma mark •• internal screen primitives
  389.  
  390. // ---------------------------------------------------------------------------
  391. //        • ClearToEOL
  392. // ---------------------------------------------------------------------------
  393. //    Clear screen from the specified position to the end of that line.
  394.  
  395. void
  396. CTerminalPane::ClearToEOL(
  397.     const TermCharT& inStartChar)                // where to start clearing
  398. {
  399.     TermCharT theChar = inStartChar;
  400.     
  401.     InvalChar(theChar, mCols - theChar.col);
  402.     while (theChar.col < maxX)                        //! TEMPORARY: bypass PutCharAt
  403.         mScreenBfr[theChar.row][theChar.col++] = ' ';
  404. }
  405.  
  406.  
  407. // ---------------------------------------------------------------------------
  408. //        • ClearToEOS
  409. // ---------------------------------------------------------------------------
  410. //    Clear screen from the specified position to the end of the screen.
  411.  
  412. void
  413. CTerminalPane::ClearToEOS(
  414.     const TermCharT& inStartChar)
  415. {
  416.     TermCharT theChar = inStartChar;
  417.     while (theChar.row < mRows) {
  418.         ClearToEOL(theChar);
  419.         theChar.row++;
  420.         theChar.col = 0;
  421.     }
  422. }
  423.  
  424.  
  425. // ---------------------------------------------------------------------------
  426. //        • ScrollTerm
  427. // ---------------------------------------------------------------------------
  428.  
  429. void
  430. CTerminalPane::ScrollTerm()
  431. {
  432.     ::BlockMoveData(&mScreenBfr[1][0], &mScreenBfr[0][0], (maxY-1) * maxX);
  433.     TermCharT theChar = { mRows-1, 0 };
  434.     ClearToEOL(theChar);
  435.     Refresh();
  436. }
  437.  
  438.  
  439. // ---------------------------------------------------------------------------
  440. //        • CursorMoved
  441. // ---------------------------------------------------------------------------
  442. //    Update the cursor display. If several characters are being displayed at once on the same
  443. //    line, we invalidate the entire line. (This is assuming that several more characters will
  444. //    arrive before we can process the next update event; thus we can reduce the number of
  445. //    calls to InvalChar.)
  446.  
  447. void
  448. CTerminalPane::CursorMoved()
  449. {
  450.     mCursorBlinkVisible = true;
  451.     mCursorBlinkTick = LMGetTicks();
  452.  
  453.     if (mPreviousCursor.row == mCurrentCursor.row) {
  454.         switch (mCharsToInvalLine) {
  455.             case 0:                            // optimization to reduce calls to InvalRect()
  456.                 break;
  457.             case 1:
  458.                 InvalChar(mCurrentCursor, mCols - mCurrentCursor.col);
  459.                 mCharsToInvalLine = 0;
  460.                 break;
  461.             default:
  462.                 InvalChar(mPreviousCursor);
  463.                 InvalChar(mCurrentCursor);
  464.                 mCharsToInvalLine--;
  465.         }
  466.     }
  467.     else {
  468.         InvalChar(mPreviousCursor);
  469.         InvalChar(mCurrentCursor);
  470.         mCharsToInvalLine = resetInvalsChar;
  471.     }
  472.     
  473.     mPreviousCursor = mCurrentCursor;
  474. }
  475.  
  476.  
  477. // ---------------------------------------------------------------------------
  478. //        • InvalChar
  479. // ---------------------------------------------------------------------------
  480.  
  481. void
  482. CTerminalPane::InvalChar(
  483.     const TermCharT&    inChar,                // cursor position to invalidate
  484.     CharIndexT            inNumChars)            // (optional, default=1) number of chars to invalidate
  485. {
  486.     Rect charRect;
  487.     FocusDraw();
  488.     if (FetchLocalCharFrame(inChar, charRect, inNumChars, 1)) {
  489.         LocalToPortPoint(topLeft(charRect));
  490.         LocalToPortPoint(botRight(charRect));
  491.         InvalPortRect(&charRect);
  492.     }
  493. }
  494.  
  495.  
  496. // ---------------------------------------------------------------------------
  497. //        • InvertChar
  498. // ---------------------------------------------------------------------------
  499. //    Invert the rectangle for a single character (usually the cursor position).
  500.  
  501. void
  502. CTerminalPane::InvertChar(
  503.     const TermCharT& inChar)
  504. {
  505.     Rect charFrame;
  506.     if (FetchLocalCharFrame(inChar, charFrame)) {
  507.         ::InvertRect(&charFrame);
  508.         if (!IsActive()) {
  509.             ::InsetRect(&charFrame, 1, 1);
  510.             ::InvertRect(&charFrame);
  511.         }
  512.     }
  513. }
  514.  
  515.  
  516. // ===========================================================================
  517.  
  518. #pragma mark -
  519. #pragma mark •• character-to-pixel conversions
  520.  
  521. // ---------------------------------------------------------------------------
  522. //        • FetchCharHitBy
  523. // ---------------------------------------------------------------------------
  524.  
  525. void
  526. CTerminalPane::FetchCharHitBy(
  527.     const PP_PowerPlant::SPoint32&    inImagePt,
  528.     TermCharT&        outChar)
  529. {
  530.     outChar.row = (inImagePt.v) / mRowHeight;
  531.     outChar.col = (inImagePt.h) / mColWidth;
  532. }
  533.  
  534.  
  535. // ---------------------------------------------------------------------------
  536. //        • FetchLocalCharFrame
  537. // ---------------------------------------------------------------------------
  538.  
  539. bool
  540. CTerminalPane::FetchLocalCharFrame(
  541.     const TermCharT&    inChar,                // character coordinates (top-left if fetching multiple chars)
  542.     Rect&                outCharFrame,        // returns local frame of character
  543.     CharIndexT            inNumCols,            // (optional, default=1) number of columns to fetch
  544.     CharIndexT            inNumRows)            // (optional, defualt=1) number of rows to fetch
  545. {
  546.     
  547.     // Get top-left in image coordinates.
  548.     
  549.     PP_PowerPlant::SPoint32 charImage;
  550.     charImage.h = (inChar.col) * mColWidth;
  551.     charImage.v = (inChar.row) * mRowHeight;
  552.     
  553.     // If character intersects the frame, convert to local coordinates.
  554.     
  555.     bool intersectsFrame = ImageRectIntersectsFrame(charImage.h, charImage.v,
  556.                             charImage.h + (mColWidth * inNumCols),
  557.                             charImage.v + (mRowHeight * inNumRows));
  558.     if (intersectsFrame) {
  559.         ImageToLocalPoint(charImage, topLeft(outCharFrame));
  560.         outCharFrame.right = outCharFrame.left + (mColWidth * inNumCols);
  561.         outCharFrame.bottom = outCharFrame.top + (mRowHeight * inNumRows);
  562.     }
  563.     return intersectsFrame;
  564. }
  565.  
  566.  
  567. // ===========================================================================
  568.  
  569. #pragma mark -
  570. #pragma mark •• direct access to screen buffer
  571.  
  572. // ---------------------------------------------------------------------------
  573. //        • PutCharAt
  574. // ---------------------------------------------------------------------------
  575. //    Place the character in the screen buffer.
  576.  
  577. void
  578. CTerminalPane::PutCharAt(
  579.     const TermCharT&    inCharPosition,
  580.     char                inChar)
  581. {
  582.     mScreenBfr[inCharPosition.row][inCharPosition.col] = inChar;
  583. }
  584.  
  585.  
  586. // ---------------------------------------------------------------------------
  587. //        • GetCharFrom
  588. // ---------------------------------------------------------------------------
  589. //    Fetch the character at a particular position.
  590.  
  591. char
  592. CTerminalPane::GetCharFrom(
  593.     const TermCharT& inCharPosition) const
  594. {
  595.     return mScreenBfr[inCharPosition.row][inCharPosition.col];
  596. }
  597.  
  598.  
  599. // ===========================================================================
  600.  
  601. #pragma mark -
  602. #pragma mark •• initialization
  603.  
  604. // ---------------------------------------------------------------------------
  605. //        • InitTerm
  606. // ---------------------------------------------------------------------------
  607. //    Set parameters for terminal display.
  608. //    Should only be called by constructors for CTerminalPane.
  609.  
  610. void
  611. CTerminalPane::InitTerm()
  612. {
  613.     //! TEMPORARY: feed values from hard-wired parameters
  614.     
  615.     mRows = maxY;
  616.     mCols = maxX;
  617.  
  618.     mRowHeight = pixelsY;
  619.     mColWidth = pixelsX;
  620.  
  621.     mPreviousCursor.row = mPreviousCursor.col = 0;
  622.     mCharsToInvalLine = resetInvalsChar;
  623.  
  624.     mBlinkCursor = false;
  625.     mCursorBlinkVisible = true;
  626.     mCursorBlinkTick = 0L;
  627.  
  628.     DoClearScreen();
  629. }
  630.